package org.atlantis.net.base.echoudp;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/*
 * 创建一个典型的UDP客户端，主要分为以下3个大致步骤：
 * 1.创建一个DatagramSocket实例，可以选在对本地地址和端口号进行设置。
 * 2.使用DatagramSocket类的send和receive方法来发送和接收DatagramPacket实例，进行通信。
 * 3.通信完成后，使用DatagramSocket类的close方法来销毁该套接字。
 * 注意：与Socket不同，DatagramSocket实例在创建时不需要制定目的地址。
 */
public class UDPEchoClient {

	private static final int TIMEOUT = 3000;
	private static final int MAXTRIES = 5;
	private static final String SERVER_HOST = "localhost";
	private static final int SERVER_PORT = 10006;

	// 要发送的消息
	private static final String msg = "click-click";

	public static void main(String[] args) throws Exception {
		InetAddress serverAddr = InetAddress.getByName(SERVER_HOST);
		byte[] bytesToSend = msg.getBytes();
		// 服务器的地址和端口不是由DatagramSocket指定的（与TCP最大的不同），而是包含在DatagramPacket
		DatagramSocket socket = new DatagramSocket();
		// 设置socket.receive的超时时间
		socket.setSoTimeout(TIMEOUT);

		// 服务器的地址和端口是放在DatagramPacket中的，和数据一起发送出去
		DatagramPacket sendPacket = new DatagramPacket(bytesToSend,
				bytesToSend.length, serverAddr, SERVER_PORT);
		// 用来存放从服务器接收来的数据
		DatagramPacket receivePacket = new DatagramPacket(
				new byte[bytesToSend.length], bytesToSend.length);

		/*
		 * 由于使用UDP传送数据会有丢失的可能，并且在使用receive接收数据时会阻塞住。所以如果数据丢失了，
		 * 那么receive就不能接收到数据，就会一直阻塞下去，所以在上面使用socket.setSoTimeout设置了阻塞 超时时间。
		 * 而tries是表示尝试次数，如果一次失败了，那么就再发送一次数据到服务器，然后等待回执，总共尝试5次。
		 */
		int tries = 0;
		boolean receivedReponse = false;
		do {
			socket.send(sendPacket); // 向服务器发送数据
			try {
				socket.receive(receivePacket); // 等待接收服务器的回执数据
				if (!receivePacket.getAddress().equals(serverAddr)) { // 检查是否从正确的服务器上回执的信息
					throw new IOException(
							"Received packet from an unknow server");
				}
				receivedReponse = true;
			} catch (InterruptedIOException e) {
				tries += 1;
				System.err.println("Timed out, " + (MAXTRIES - tries)
						+ " more tries...");
			}
		} while ((!receivedReponse) && (tries < MAXTRIES));
		
		if (receivedReponse) {
			System.out.println("Received: " + new String(receivePacket.getData()));
		} else {
			System.out.println("No reponse o(.“.)o giving up.");
		}
		socket.close();
	}
}
